package com.chushouya.manager.service.common.impl;

import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.general.framework.core.lang.*;
import com.chushouya.common.constant.RedisKeyEnum;
import com.chushouya.manager.config.SmsProperties;
import com.chushouya.manager.constants.SmsSceneEnum;
import com.chushouya.manager.constants.SmsTemplateEnum;
import com.chushouya.manager.dto.admin.sms.SmsBody;
import com.chushouya.manager.dto.admin.sms.SmsDTO;
import com.chushouya.manager.service.common.SmsService;
import com.general.framework.core.exception.Ex;
import com.general.framework.data.redis.RedisCache;
import com.general.framework.data.redis.RedisCachePlus;
import com.general.framework.web.utils.IpUtils;
import com.general.framework.web.utils.ServletUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpStatus;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;



@Slf4j
@Service
@RefreshScope
public class SmsServiceImpl implements SmsService {

    /**
     * 是否发送短信
     */
    @Value("${com.chushouya.manager.sms.sendSms:true}")
    private Boolean sendSms;

    /**
     * 是否校验验证码(测试环境关闭，将不发送/不验证验证码)
     */
    @Value("${com.chushouya.manager.sms.validateSmsCode:true}")
    private Boolean validateSmsCode;

    /**
     * 白名单用户(配置需慎重:不发送/不验证验证码，用于线上排查模拟用户,或虚拟手机号注册)
     */
    @Value("${com.chushouya.manager.sms.ignorePhones}")
    private String ignorePhones;

    @Resource
    private SmsProperties smsProperties;

    @Resource
    private RedisCache redisCache;

    @Resource
    private RedisCachePlus redisCachePlus;

    private IAcsClient acsClient;

    private static final String SIGN = "出手科技";

    /**
     * 初始化
     */
    @PostConstruct
    public void init() {
        final IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", smsProperties.getAccessKeyId(), smsProperties.getAccessKeySecret());
        acsClient = new DefaultAcsClient(profile);
    }

    @Override
    public boolean sendDynamicTemplateSms(SmsDTO dto) {
        String phone = dto.getPhone();
        String templateCode = dto.getTemplateCode();
        if (Strings.isBlank(phone) || Strings.isBlank(templateCode)) {
            return false;
        }
        log.info("===>发送短信到手机:{}, 模板:{}, 模板参数:{}", phone, templateCode, dto.getParams());
        if (!sendSms) {
            log.info("配置短信不发送: 手机号码:{}, 模板:{}, 模板参数:{}", phone, templateCode, dto.getParams());
            return true;
        }
        return this.sendMsg(phone, templateCode, SIGN, dto.getParams());
    }


    @Override
    public void sendSmsCode(final String scene, final String phone, final String signName) {
        log.debug("sendSms:{}, validateSmsCode:{}, ignorePhones:{}", this.sendSms, this.validateSmsCode, this.ignorePhones);
        Check.hasText(phone, "手机号不能为空");
        log.info("===>发送短信验证码到手机:{},场景:{},签名:{}", phone, scene, SmsSceneEnum.getName(String.valueOf(scene)));
        Validate.isEmpty(SmsSceneEnum.getName(String.valueOf(scene)), "场景不存在");
        this.validateIpTimes();
        this.validateCodeTimes(scene, phone);

        final String smsCode = IdTakes.takeRandomNum(4);
        final SmsBody smsBody = SmsBody.builder(phone).setTemplate(SmsTemplateEnum.APP_LOGIN_CODE)
                .setSignName(signName).addParam("code", smsCode);
//                .addParam("product", SmsSceneEnum.getName(String.valueOf(scene)));
        if (this.validateSmsCode) {
            if (!(Strings.isNotBlank(ignorePhones) && ignorePhones.contains(phone))) {
                // 不在白名单
                this.sendSmsAsync(smsBody);
            } else {
                log.info("Tip: The phone {} already exists in ignorePhones, skip send sms", phone);
            }
        } else {
            log.info("Tip: The config com.chushouya.manager.setting.validateSmsCode is false, skip send sms");
        }
        // 加入缓存
        redisCache.setCacheObject(RedisKeyEnum.SMS_VALIDATE_CODE, smsCode, scene, phone);
    }

    @Override
    public boolean validateSmsCode(final String scene, final String phone, final String smsCode) {
        Check.hasText(phone, "手机号不能为空");
        Check.hasText(smsCode, "验证码不能为空");
        if (!validateSmsCode) {
            log.info("Tip: The config com.chushouya.manager.setting.validateSmsCode is false, skip validate code");
            return true;
        }
        if (Strings.isNotBlank(ignorePhones) && ignorePhones.contains(phone)) {
            log.info("Tip: The phone {} already exists in ignorePhones, skip validate code", phone);
            return true;
        }

        final String cacheKey = RedisKeyEnum.SMS_VALIDATE_CODE.value(scene, phone);
        log.info("===>验证短信验证码,场景:{},手机号:{},验证码:{} {}", scene, phone, smsCode,cacheKey);
        final String cacheValue = redisCache.getCacheObject(cacheKey);
        if (Strings.isBlank(cacheValue)) {
            throw Ex.business("验证码已过期");
        }

        final boolean validatePass = Strings.equals(smsCode, cacheValue);
        if (!validatePass) {
            throw Ex.business("验证码错误");
        }
        // 移除缓存
        redisCache.deleteObject(cacheKey);
        return true;
    }

    @Override
    @Async
    public void sendSmsAsync(final SmsBody smsBody) {
        this.sendSmsHandler(smsBody);
    }


    @Override
    public boolean sendSms(final SmsBody smsBody) {
        return this.sendSmsHandler(smsBody);
    }

    private boolean sendSmsHandler(SmsBody smsBody) {
        log.info("===>发送短信到手机:{},短信模板:{},参数:{}", smsBody.getPhone(), smsBody.getTemplate().getContent(), Jsons.toJSONString(smsBody.getParams()));
        if (!this.sendSms) {
            log.info("===>Tip: 短信配置com.chushouya.manager.setting.sendSms为false,当前跳过短信发送");
            return true;
        }
        Map<String, String> paramMap = null;
        if (Lists.isNotEmpty(smsBody.getParams())) {
            paramMap = new HashMap<>(smsBody.getParams().size());
            for (SmsBody.Param param : smsBody.getParams()) {
                paramMap.put(param.getKey(), param.getValue());
            }
        }
        return this.sendMsg(smsBody.getPhone(), smsBody.getTemplate().getCode(), Strings.defaultString(smsBody.getSignName(), SIGN), paramMap);
    }





    /**
     * 发送短信
     */
    private boolean sendMsg(final String phone, final String templateCode, final String signName, final Map<String, String> paramMap) {
        Check.hasText(phone, "手机号不能为空");
        Check.hasText(templateCode, "模板编号不能为空");
        // 手机号格式验证
        Check.mobile(phone, "手机号格式错误");
        final CommonRequest request = new CommonRequest();
        request.setSysMethod(MethodType.POST);
        request.setSysDomain("dysmsapi.aliyuncs.com");
        request.setSysVersion("2017-05-25");
        request.setSysAction("SendSms");
        request.putQueryParameter("RegionId", "cn-hangzhou");
        request.putQueryParameter("PhoneNumbers", phone);
        request.putQueryParameter("TemplateCode", templateCode);
        request.putQueryParameter("SignName", signName);
        request.putQueryParameter("OutId", phone + "_" + System.currentTimeMillis());
        if (!(paramMap == null || paramMap.isEmpty())) {
            request.putQueryParameter("TemplateParam", Jsons.toJsonString(paramMap));
        }
        try {
            final CommonResponse response = acsClient.getCommonResponse(request);
            if (response == null) {
                log.error("发送短信失败，响应数据为空，手机号={}，templateCode={}", phone, templateCode);
                return false;
            }
            if (response.getHttpStatus() != HttpStatus.SC_OK) {
                log.error("发送短信失败，响应数据异常，手机号={}，templateCode={}，response={}", phone, templateCode, Jsons.toJsonString(response));
                return false;
            }
            log.info("===>发送短信响应:{}", response.getData());
        } catch (ClientException e) {
            log.error("发送短信出错，手机号={}，templateCode={}, ex={}", phone, templateCode, e);
            return false;
        }
        return true;
    }


    /**
     * 登录次数验证
     *
     * @param scene 场景
     * @param phone 手机
     */
    private void validateCodeTimes(final String scene, final String phone) {
        if (this.sendSms) {
            final String cacheKey = RedisKeyEnum.SMS_VALIDATE_CODE_TIMES.value(scene, phone);
            // 判断是否超过次数
            final Long times = redisCachePlus.getAndIncrement(cacheKey);
            redisCache.expire(cacheKey, RedisKeyEnum.SMS_VALIDATE_CODE_TIMES.expire(), TimeUnit.SECONDS);
            if (times >= Numbers.INTEGER_THREE) {
                log.error("当前IP操作太频繁，请稍后再试:{},{}", scene, phone);
                throw Ex.business("您操作太频繁，请稍后再试");
            }
        }
    }

    private void validateIpTimes() {
        if (this.sendSms) {
            String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
            final String[] limitIps = ip.split(",");
            if (Objects.nonNull(limitIps[0])){
                ip = limitIps[0];
            }
            final String cacheKey = RedisKeyEnum.SMS_VALIDATE_IP_TIMES.value(ip);
            // 判断是否超过次数
            Long times = redisCachePlus.getAndIncrement(cacheKey);
            redisCache.expire(cacheKey, RedisKeyEnum.SMS_VALIDATE_IP_TIMES.expire(), TimeUnit.SECONDS);
            if (times >= 10L) {
                log.error("当前IP操作太频繁，请稍后再试:{}", ip);
                throw Ex.business("当前IP操作太频繁，请稍后再试");
            }
        }
    }

}
